package labox.innovation.gameserver.model;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;

import javolution.util.FastList;
import javolution.util.FastMap;
import labox.innovation.config.Config;
import labox.innovation.gameserver.geodata.GeoData;
import labox.innovation.gameserver.geodata.GeoEngine;
import labox.innovation.gameserver.model.actor.L2Character;
import labox.innovation.gameserver.model.actor.L2Npc;
import labox.innovation.gameserver.model.actor.L2Playable;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;
import labox.innovation.gameserver.model.actor.position.ObjectPosition;
import labox.innovation.gameserver.model.zone.L2ZoneType;
import labox.innovation.gameserver.model.zone.type.L2PeaceZone;
import labox.innovation.gameserver.model.zone.type.L2TownZone;
import labox.innovation.gameserver.timer.Timer;
import labox.innovation.util.Point2D;

/**
 * 游戏场景模型
 * 
 */
public final class Scene {

	private static final Logger _log = LoggerFactory.getLogger(Scene.class.getName());

	public static final int SHIFT_BY = 7;

	public static final int TILEWH = (int) Math.pow(2, SHIFT_BY);

	private SceneRegion[][] _worldRegions;

	/**
	 *此区域内所有可玩的对象（包括玩家和召唤类）
	 */
	private Map<Long, L2Playable> _allPlayable;

	/**
	 * 此区域内所有的可见对象
	 */
	private Map<Long, L2Object> _visibleObjects;

	/**
	 * 场景上地带容器
	 */
	private final FastList<L2ZoneType> _zones;

	/**
	 * 场景ID
	 */
	private int _id;

	/**
	 * 场景上区域的列的数量
	 */
	private int region_column;

	/**
	 * 场景上区域的行的数量
	 */
	private int region_row;

	/**
	 * 场景上NPC出生点列表
	 */
	private SpawnTable _spawns;

	/**
	 * 地形数据
	 */
	private GeoData _geo;

	/**
	 * 发送给客户端的地图数据
	 */
	private byte[] _mapdata;

	/**
	 * 场景是否激活
	 */
	private boolean _active;

	/**
	 * 场景名称
	 */
	private String _name;

	/**
	 * 场景描述
	 */
	private String _disc;

	/**
	 * 场景经验加成倍率
	 */
	private float _expRate;

	/**
	 * 场景能否pk
	 */
	private boolean _canPk;

	/**
	 * 在当前场景人族请求复活后复活到的场景ID
	 */
	private int _humanReviveScene;

	/**
	 * 在当前场景人族请求复活后复活到的场景X坐标
	 */
	private int _humanReviveX;

	/**
	 * 在当前场景人族请求复活后复活到的场景Y坐标
	 */
	private int _humanReviveY;
	
	/**
	 * 在当前场景巫族请求复活后复活到的场景ID
	 */
	private int _ELFReviveScene;
	
	/**
	 * 在当前场景巫族请求复活后复活到的场景X坐标
	 */
	private int _ELFReviveX;
	
	/**
	 * 在当前场景巫族请求复活后复活到的场景Y坐标
	 */
	private int _ELFReviveY;

	/**
	 * 传送到场景上的X坐标
	 */
	private int _teleX;

	/**
	 * 传送到场景上的Y坐标
	 */
	private int _teleY;

	/**
	 * 首次开启场景的计时器
	 */
	private Timer _firstTimer;

	/**
	 * 场景开放的周期
	 */
	private long _cycle;

	/**
	 * 周期开启场景的计时器
	 */
	private Timer _cycleTimer;

	/**
	 * 场景开放的持续时间
	 */
	private long _duration;

	/**
	 * 关闭场景的计时器
	 */
	private Timer _durationTimer;

	/**
	 * 职业限制，在表中的职业可以进入
	 */
	private int[] _classRestrict;

	/**
	 * 性别限制
	 */
	private int _sexRestrict;

	/**
	 * 道具限制
	 */
	private int[] _itemRestrict;

	private int _teleport2Scene = -1;

	private int _teleport2X;

	private int _teleport2Y;
	
	/**
	 * 在客户端上显示x坐标
	 */
	private int _screenX;
	
	/**
	 * 在客户端上显示y坐标
	 */
	private int _screenY;

	/**
	 * Constructor of L2World.<BR>
	 * <BR>
	 */
	public Scene(int id, GeoData geo) {
		_id = id;
		_geo = geo;
		_allPlayable = new FastMap<Long, L2Playable>();
		_visibleObjects = new FastMap<Long, L2Object>();
		_zones = new FastList<L2ZoneType>();
		initRegions();
	}

	/**
	 * 初始化场景上所有的L2WorldRegion和他周围region的列表.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * All surrounding L2WorldRegion are identified in <B>_surroundingRegions</B> of the selected L2WorldRegion in order to scan a large area around a L2Object<BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Constructor of L2World</li><BR>
	 * 
	 */
	private void initRegions() {
		if (_log.isDebugEnabled())
			_log.debug("FuzeScene: Setting up Scene Regions");

		region_column = (getWidth() & (TILEWH - 1)) == 0 ? getWidth() / TILEWH : getWidth() / TILEWH + 1;

		region_row = (getHeight() & (TILEWH - 1)) == 0 ? getHeight() / TILEWH : getHeight() / TILEWH + 1;

		_worldRegions = new SceneRegion[region_column][region_row];
		for (int i = 0; i < region_column; i++) {//初始化所有的地区
			for (int j = 0; j < region_row; j++) {
				_worldRegions[i][j] = new SceneRegion(i, j, this);
			}
		}

		if (_log.isDebugEnabled())
			_log.debug("Scene: (" + getRegion_column() + " by " + getRegion_row() + ") Scene Region Grid set up.");

	}

	public void setName(String name) {
		_name = name;
	}

	public String getName() {
		return _name;
	}

	public FastList<L2ZoneType> getZones() {
		return _zones;
	}

	public void addZone(L2ZoneType zone) {
		_zones.add(zone);
	}

	public void removeZone(L2ZoneType zone) {
		_zones.remove(zone);
	}

	/**
	 * 检测L2Character与区域内所有地带的关系，如果这个L2Character在地带设置的影响参数范围内，那么检测L2Character进出每个地带的逻辑
	 * 
	 * @param character
	 */
	public void revalidateZones(L2Character character) {
		// do NOT update the world region while the character is still in the process of teleporting
		// Once the teleport is COMPLETED, revalidation occurs safely, at that time.

		if (character.isTeleporting())
			return;

		for (L2ZoneType z : _zones) {
			if (z != null)
				z.revalidateInZone(character);
		}
	}

	public void removeFromZones(L2Character character) {
		for (L2ZoneType z : _zones) {
			if (z != null)
				z.removeCharacter(character);
		}
	}

	/**
	 * 是否包含指定的地带ID
	 * 
	 * @param zoneId
	 * @return
	 */
	public boolean containsZone(int zoneId) {
		for (L2ZoneType z : _zones) {
			if (z.getId() == zoneId) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 
	 * @param skill
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean checkEffectRangeInsidePeaceZone(L2Skill skill, final int x, final int y) {
		final int range = skill.getEffectRange();
		final int up = y + range;
		final int down = y - range;
		final int left = x + range;
		final int right = x - range;

		for (L2ZoneType e : _zones) {
			if ((e instanceof L2TownZone && ((L2TownZone) e).isPeaceZone()) || e instanceof L2PeaceZone) {
				if (e.isInsideZone(x, up))
					return false;

				if (e.isInsideZone(x, down))
					return false;

				if (e.isInsideZone(left, y))
					return false;

				if (e.isInsideZone(right, y))
					return false;

				if (e.isInsideZone(x, y))
					return false;
			}
		}
		return true;
	}

	public void onDeath(L2Character character) {
		for (L2ZoneType z : _zones) {
			if (z != null)
				z.onDieInside(character);
		}
	}

	public void onRevive(L2Character character) {
		for (L2ZoneType z : getZones()) {
			if (z != null)
				z.onReviveInside(character);
		}
	}

	/**
	 * Add the L2Object in the L2ObjectHashSet(L2Object) _visibleObjects containing L2Object visible in this L2WorldRegion <BR>
	 * If L2Object is a L2PcInstance, Add the L2PcInstance in the L2ObjectHashSet(L2PcInstance) _allPlayable containing L2PcInstance of all player in game in this L2WorldRegion <BR>
	 * Assert : object.getCurrentWorldRegion() == this
	 */
	public void addVisibleObject(L2Object object) {
		if (object == null)
			return;

		_visibleObjects.put(object.getObjectId(), object);

		if (object instanceof L2Playable) {
			_allPlayable.put(object.getObjectId(), (L2Playable) object);
			revalidateZones((L2Character) object); //检查进入Zone的情况
		}

		ObjectPosition op = object.getPosition();
		op.setScene(this);//设置对象所在的场景

		SceneRegion region = getRegion(object.getPosition().getScenePosition());//计算出应该进入的region

		//设置此对象在这个region上
		op.setSceneRegion(region);

		//更新knowlist
		op.updataRegion(region.get_tilex() - 1, region.get_tiley() - 1, region.get_tilex() + 1, region.get_tiley() + 1);

		//设置region拥有这个对象
		region.addVisibleObject(object);
	}

	/**
	 * Remove the L2Object from the L2ObjectHashSet(L2Object) _visibleObjects in this L2WorldRegion <BR>
	 * <BR>
	 * 
	 * If L2Object is a L2PcInstance, remove it from the L2ObjectHashSet(L2PcInstance) _allPlayable of this L2WorldRegion <BR>
	 * Assert : object.getCurrentWorldRegion() == this || object.getCurrentWorldRegion() == null
	 */
	public void removeVisibleObject(L2Object object) {
		if (object == null)
			return;
		_visibleObjects.remove(object.getObjectId());

		if (object instanceof L2Playable) {
			_allPlayable.remove(object.getObjectId());
			removeFromZones((L2Character) object); //从Zone区里面删除此对象
		}

		ObjectPosition op = object.getPosition();

		//设置对象离开他所在的region
		SceneRegion sr = op.getSceneRegion();
		if (sr != null) {
			sr.removeVisibleObject(object);
		}

		//向对象的knownlist中对象通知删除此对象
		object.getKnownList().removeAllKnownRegion();

		op.setSceneRegion(null);
		//这里本来应该设置成null，但是定时回写线程此时回写会导致获得的scene是null，所以在用户离开这个场景的时候，在没有进入到新的场景前其实还在原来的场景
		//op.setScene(null);
	}

	public Map<Long, L2Playable> getVisiblePlayable() {
		return _allPlayable;
	}

	public Map<Long, L2Object> getVisibleObjects() {
		return _visibleObjects;
	}

	public L2Object getObjects(L2Spawn spawn) {
		Collection<L2Object> vNPC = _visibleObjects.values();
		for (L2Object obj : vNPC) {
			if (obj instanceof L2Npc) {

				L2Npc target = (L2Npc) obj;
				L2Spawn s = target.getSpawn();
				if (s == spawn) {
					return target;
				}
				if (_log.isDebugEnabled())
					_log.debug("Removed NPC :" + target.getObjectId());
			}
		}

		return null;
	}

	/**
	 * 删除所有NPC和NPC的出生点
	 */
	public void deleteVisibleNpcSpawns() {
		Collection<L2Object> vNPC = _visibleObjects.values();

		for (L2Object obj : vNPC) {
			if (obj instanceof L2Npc) {
				L2Npc target = (L2Npc) obj;
				target.deleteMe();
				L2Spawn spawn = target.getSpawn();
				if (spawn != null) {
					spawn.stopRespawn();
					_spawns.deleteSpawn(spawn);
				}
				if (_log.isDebugEnabled())
					_log.debug("Removed NPC " + target.getObjectId());
			}
		}
	}

	/**
	 * Calculate the current L2WorldRegions of the object according to its position (x,y).<BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Set position of a new L2Object (drop, spawn...)</li> <li>Update position of a L2Object after a mouvement</li><BR>
	 * 
	 * @param Point3D
	 *            point position of the object
	 */
	public SceneRegion getRegion(Point2D point) {
		return _worldRegions[point.getX() >> SHIFT_BY][point.getY() >> SHIFT_BY];
	}

	public SceneRegion getRegionByXY(int x, int y) {
		return _worldRegions[x >> SHIFT_BY][y >> SHIFT_BY];
	}

	public SceneRegion getRegion(int column, int row) {
		if (column < 0 || column > region_column - 1)
			return null;
		if (row < 0 || row > region_row - 1)
			return null;
		return _worldRegions[column][row];
	}

	/**
	 * Returns the whole 2d array containing the world regions used by ZoneData.java to setup zones inside the world regions
	 * 
	 * @return
	 */
	public SceneRegion[][] getAllWorldRegions() {
		return _worldRegions;
	}

	/**
	 * Check if the current L2WorldRegions of the object is valid according to its position (x,y).<BR>
	 * <BR>
	 * 
	 * <B><U> Example of use </U> :</B><BR>
	 * <BR>
	 * <li>Init L2WorldRegions</li><BR>
	 * 
	 * @param x
	 *            X position of the object
	 * @param y
	 *            Y position of the object
	 * 
	 * @return True if the L2WorldRegion is valid
	 */
	private boolean validRegion(int x, int y) {
		return (x >= 0 && x <= getWidth() && y >= 0 && y <= getHeight());
	}

	public void set_id(int _id) {
		this._id = _id;
	}

	public int get_id() {
		return _id;
	}

	public int getWidth() {
		return _geo.getWidth();

	}

	public int getHeight() {
		return _geo.getHeight();
	}

	public int getRegion_column() {
		return region_column;
	}

	public int getRegion_row() {
		return region_row;
	}

	public GeoData getGeoData() {
		return _geo;
	}

	public byte[] getMapData() {
		return _mapdata;
	}

	public static final Scene fromXML(Element e, ArrayList<Scene> scenes) throws Exception{
		StringBuilder sb = new StringBuilder();
		String path = e.getAttribute("map");
		path = path.substring(path.lastIndexOf('\\') + 1);
		sb.append(Config.DATAPACK_ROOT.getAbsolutePath()).append("/Map/").append(path);
		File f = new File(sb.toString()).getAbsoluteFile();
		ByteArrayOutputStream bais = new ByteArrayOutputStream();
		GeoEngine ge = GeoEngine.fromXML(f, bais);
		if (ge == null)
			return null;
		Scene scn = new Scene(scenes.size(), ge);
		scenes.add(scn);//把场景过早的加入世界管理器，为了初始化后面的NPC对象
		scn._spawns = SpawnTable.fromXML(scn, e);

		//预处理场景上所有的动画ID，收集场景上的动画ID发送给客户端
		ArrayList<Integer> al = new ArrayList<Integer>();
		for (Map.Entry<Integer, L2Spawn> en : scn._spawns.getSpawnTable().entrySet()) {
			int id = en.getValue().getTemplate().get_animationId();
			if (!al.contains(id)) {
				al.add(id);
			}
		}
		bais.write(al.size());
		for (int i = -1; ++i < al.size();) {
			bais.write(al.get(i));
		}

		scn._mapdata = bais.toByteArray();

		return scn;
	}

	/**
	 * 设置场景开启关闭的逻辑
	 * 
	 * @param _active
	 */
	public void setActive(boolean _active) {
		this._active = _active;
		if (_active) {//如果激活了场景,设置关闭场景的时间
			if (_duration > 0) {
				_durationTimer = new Timer(_duration, true, new Runnable() {
					public void run() {
						setActive(false);
						_durationTimer = null;
					}
				});
				_durationTimer.start();
			}
		} else {//关闭场景时检查，是否要传送玩家离开
			if (_teleport2Scene > 0) {
				for (L2Playable playable : _allPlayable.values()) {
					if (playable instanceof FuzePcInstance) {
						//传送出玩家
						playable.setInstanceId(0);
						playable.teleToLocation(_teleport2Scene, _teleport2X, _teleport2Y);
					}
				}
			}
		}
	}

	/**
	 * 场景是否激活
	 * 
	 * @return
	 */
	public boolean isActive() {
		return _active;
	}

	/**
	 * 设置场景的描述
	 * 
	 * @param _disc
	 *            描述
	 */
	public void setDisc(String _disc) {
		this._disc = _disc;
	}

	/**
	 * 获得场景的描述
	 * 
	 * @return
	 */
	public String getDisc() {
		return _disc;
	}

	/**
	 * 获得场景的经验加成倍率
	 * 
	 * @param _expRate
	 *            倍率
	 */
	public void setExpRate(float _expRate) {
		this._expRate = _expRate;
	}

	/**
	 * 获得场景的经验加成倍率
	 * 
	 * @return
	 */
	public float getExpRate() {
		return _expRate;
	}

	/**
	 * 设置是否能够PK
	 * 
	 * @param _canPk
	 */
	public void setCanPk(boolean canPk) {
		_canPk = canPk;
	}

	/**
	 * 获得场景能否pk
	 * 
	 * @return
	 */
	public boolean isCanPk() {
		return _canPk;
	}

	/**
	 * 设置人族复活场景ID
	 * 
	 * @param _reviveScene
	 */
	public void setHumanReviveScene(int _reviveScene) {
		_humanReviveScene = _reviveScene;
	}

	/**
	 * 获得人族复活的场景ID
	 * 
	 * @return
	 */
	public int getHumanReviveScene() {
		return _humanReviveScene;
	}

	/**
	 * 设置人族复活的XY坐标
	 * 
	 * @param _reviveX
	 * @param _reviveY
	 */
	public void setHumanRevive2X(int _reviveX, int _reviveY) {
		_humanReviveX = _reviveX;
		_humanReviveY = _reviveY;
	}

	public int getHumanReviveX() {
		return _humanReviveX;
	}

	public int getHumanReviveY() {
		return _humanReviveY;
	}
	
	/**
	 * 设置巫族复活场景ID
	 * 
	 * @param _reviveScene
	 */
	public void setELFReviveScene(int _reviveScene) {
		_ELFReviveScene = _reviveScene;
	}

	/**
	 * 获得巫族复活的场景ID
	 * 
	 * @return
	 */
	public int getELFReviveScene() {
		return _ELFReviveScene;
	}

	/**
	 * 设置巫族复活的XY坐标
	 * 
	 * @param _reviveX
	 * @param _reviveY
	 */
	public void setELFRevive2X(int _reviveX, int _reviveY) {
		_ELFReviveX = _reviveX;
		_ELFReviveY = _reviveY;
	}

	public int getELFReviveX() {
		return _ELFReviveX;
	}

	public int getELFReviveY() {
		return _ELFReviveY;
	}
	
	public void setTele2XY(int _teleX, int _teleY) {
		this._teleX = _teleX;
		this._teleY = _teleY;
	}

	public int getTele2X() {
		return _teleX;
	}

	public int getTele2Y() {
		return _teleY;
	}

	public void setFirstStartTime(long time) {
		if (_firstTimer != null) {
			_firstTimer.cancel();
		}
		if (_cycleTimer != null) {
			_cycleTimer.cancel();
		}
		if (_durationTimer != null) {
			_durationTimer.cancel();
		}

		//延迟time时间后开启场景
		_firstTimer = new Timer(time, false, new Runnable() {
			public void run() {
				setActive(true);
				if (_cycle > 0) {//如果周期>0,设置周期开放的时间
					_cycleTimer = new Timer(_cycle, true, new Runnable() {
						public void run() {
							setActive(true);
						}
					});
					_cycleTimer.start();
				}
				_firstTimer = null;
			}
		});
		_firstTimer.start();

	}

	public void setDuration(long _duration) {
		this._duration = _duration;
	}

	public long getDuration() {
		return _duration;
	}

	public void setCycle(long _cycle) {
		this._cycle = _cycle;
	}

	public long getCycle() {
		return _cycle;
	}

	public void setSexRestrict(int _sexRestrict) {
		this._sexRestrict = _sexRestrict;
	}

	public int getSexRestrict() {
		return _sexRestrict;
	}

	public void setClassRestrict(int[] _classRestrict) {
		this._classRestrict = _classRestrict;
	}

	public int[] getClassRestrict() {
		return _classRestrict;
	}

	public void setItemRestrict(int[] _itemRestrict) {
		this._itemRestrict = _itemRestrict;
	}

	public int[] getItemRestrict() {
		return _itemRestrict;
	}

	public void setTeleport2SceneInactive(int _teleport2Scene) {
		this._teleport2Scene = _teleport2Scene;
	}

	public int getTeleport2SceneInactive() {
		return _teleport2Scene;
	}

	public void setTeleport2XYInactive(int _teleport2X, int _teleport2Y) {
		this._teleport2X = _teleport2X;
		this._teleport2Y = _teleport2Y;
	}

	public int getTeleport2XInactive() {
		return _teleport2X;
	}

	public int getTeleport2YInactive() {
		return _teleport2Y;
	}
	
	public int getScreenX(){
		return _screenX;
	}
	
	public void setScreenX(int _screenX){
		this._screenX  = _screenX;
	}
	
	public int getScreenY(){
		return _screenY;
	}
	
	public void setScreenY(int _screenY){
		this._screenY  = _screenY;
	}
	
	
}
